// eee.Sheffield.PZ.Imaging
//
// Copyright ?Ping Zou, 2007
// sg71.cherub@gmail.com

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using eee.Sheffield.PZ.Math;
using System.Text;

namespace eee.Sheffield.PZ.Imaging
{
	/// <summary>
	/// save tracking result
	/// </summary>
	public class TrackResult	
	{
		public int _count = 0;
		public int Count()
		{
			return _count;
		}
		public double _mean_r = 0;
		public double _mean_mu = 0;
		public double _mean_B = 0;

        public PZPoint _startCentrePoint = new PZPoint();
		public PZDirection _startVesselDirection = new PZDirection();
        public PZPoint _endCentrePoint = new PZPoint();
        public PZDirection _endVesselDirection = new PZDirection();

		public List<PZPoint> _cpArray = new List<PZPoint>();
		public List<PZPoint> _ep1Array = new List<PZPoint>();
        public List<PZPoint> _ep2Array = new List<PZPoint>();
        public List<double> _rArray = new List<double>();
        public List<double> _muArray = new List<double>();
        public List<double> _BArray = new List<double>();
        public List<PZDirection> _vdArray = new List<PZDirection>();

		public string _exitMsg;
        public int _layer;
        public int _index;
        public List<int> _rootIndexList = new List<int>();

        public bool _isRoot = false;
        public bool _isLeaf = false;

        // conductive capability
        public double _flow = double.NaN;
        public double _hf;
        public double _k;

        #region Constructors
        public TrackResult()
        {
        }

        public TrackResult(int layer)
        {
            _layer = layer;
        } 
        #endregion

        #region element operation
        /// <summary>
		/// add tracking result
		/// </summary>
		/// <param name="cp">center point</param>
		/// <param name="ep1">edge point 1</param>
		/// <param name="ep2">edge point 2</param>
		/// <param name="sl">scanline</param>
		public void Add (PZPoint cp, PZPoint ep1, PZPoint ep2, PZScanline sl, PZDirection vd)
		{
            if (_count == 0)
            {
                _startCentrePoint.MemCopyFrom(cp);
                _startVesselDirection.MemCopyFrom(vd);
            }
			_cpArray.Add(cp);
			_ep1Array.Add(ep1);
			_ep2Array.Add(ep2);
			_rArray.Add(sl._r);
			_muArray.Add(sl._mu);
			_BArray.Add(sl._B);
			_vdArray.Add(vd);
            _endCentrePoint.MemCopyFrom(cp);
            _endVesselDirection.MemCopyFrom(vd);
            _count = _cpArray.Count;
		} // Add()

        /// <summary>
        /// another add function
        /// </summary>
        /// <param name="cp"></param>
        /// <param name="ep1"></param>
        /// <param name="ep2"></param>
        /// <param name="vd"></param>
        /// <param name="r"></param>
        /// <param name="mu"></param>
        /// <param name="B"></param>
        public void Add(PZPoint cp, PZPoint ep1, PZPoint ep2, PZDirection vd,
            double r, double mu, double B)
        {
            if (_count == 0)
            {
                _startCentrePoint.MemCopyFrom(cp);
                _startVesselDirection.MemCopyFrom(vd);
            }
            _cpArray.Add(cp);
            _ep1Array.Add(ep1);
            _ep2Array.Add(ep2);
            _rArray.Add(r);
            _muArray.Add(mu);
            _BArray.Add(B);
            _vdArray.Add(vd);
            _endCentrePoint.MemCopyFrom(cp);
            _endVesselDirection.MemCopyFrom(vd);
            _count = _cpArray.Count;
        }

        /// <summary>
        /// remove one element at "index"
        /// </summary>
        /// <param name="index"></param>
        public void RemoveAt(int index)
        {
            if (index < 0 || index >= _count)
                PZMath_errno.ERROR("Index out of Range.");
            _cpArray.RemoveAt(index);
            _ep1Array.RemoveAt(index);
            _ep2Array.RemoveAt(index);
            _rArray.RemoveAt(index);
            _muArray.RemoveAt(index);
            _BArray.RemoveAt(index);
            _vdArray.RemoveAt(index);
            _count = _cpArray.Count;
            if (index == 0)
            {
                _startCentrePoint.MemCopyFrom(_cpArray[0]);
                _startVesselDirection.MemCopyFrom(_vdArray[0]);
            }
            if (index == _count - 1)
            {
                _endCentrePoint.MemCopyFrom(_cpArray[_count - 1]);
                _endVesselDirection.MemCopyFrom(_vdArray[_count - 1]);
            }
        } // RemoveAt()
        #endregion

        /// <summary>
        /// is current track result in another track result?
        /// </summary>
        /// <param name="tr"></param>
        /// <returns></returns>
        public bool IsIn(TrackResult tr)
        {
            bool isIn = true;
            bool currentCPIsIn = false;
            if (tr._cpArray.Count < _cpArray.Count)
                return false;
            int inCount = 0;

            foreach (PZPoint cp1 in _cpArray)
            {
                currentCPIsIn = false;
                foreach (PZPoint cp2 in tr._cpArray)
                {
                    if (cp1.Distance(cp2) < 2)
                    {
                        currentCPIsIn = true;
                        inCount++;
                        break;
                    }
                }
                //if (!currentCPIsIn)
                //{
                //    isIn = false;
                //    break;
                //}
            }
            if (inCount > _cpArray.Count / 2)
                isIn = true;
            else
                isIn = false;

            return isIn;
        } // IsIn()

        /// <summary>
        /// does current track result contain the point?
        /// </summary>
        /// <param name="cp"></param>
        /// <returns></returns>
        public bool Contains(PZPoint cp)
        {
            bool isIn = false;
            int cpArrayCountM1 = _cpArray.Count - 1;
            PZPoint cp1;

            // end points
            cp1 = _cpArray[0];
            if (cp1.Distance(cp) < 1)
                isIn = true;
            cp1 = _cpArray[cpArrayCountM1];
            if (cp1.Distance(cp) < 1)
                isIn = true;
            // others
            for (int i = 1; i < cpArrayCountM1; i++)
            {
                cp1 = _cpArray[i];
                if (cp1.Distance(cp) < _rArray[i])
                {
                    isIn = true;
                    break;
                }                
            }            
            return isIn;
        } // Contains()

        public bool Equals(TrackResult tr)
        {
            bool isEqual = true;
            if (tr._cpArray.Count != _cpArray.Count)
                return false;

            for (int i = 0; i < _cpArray.Count; i++)
            {
                if (!tr._cpArray[i].Equals(_cpArray[i]))
                {
                    isEqual = false;
                    break;
                }
            }

            return isEqual;
        } // Equals()

        public bool Check(int width, int height)
        {
            int widthM1 = width - 1;
            int heightM1 = height - 1;
            foreach (PZPoint cp in _cpArray)
            {
                if (cp.x < 0 || cp.x > widthM1 || cp.y < 0 || cp.x > heightM1)
                    return false;
            }

            return true;
        }
        /// <summary>
        /// write to file
        /// </summary>
        /// <param name="filename"></param>
        public void Write(string filename)
		{
			FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.Write);
			StreamWriter w = new StreamWriter(fs);
            // layer
            w.WriteLine(_layer);
            // index
            w.WriteLine(_index);
            // root index
            foreach (int rootIndex in _rootIndexList)
                w.Write(String.Format("{0, -5}", rootIndex));
            w.WriteLine();

            // start centre point and direction
			w.WriteLine(_startCentrePoint.ToString() + " " + _startVesselDirection.ToString());
            // track results
			for (int i = 0; i < _cpArray.Count; i ++)
			{
				w.Write (String.Format("{0, -5} ",(i + 1)));
				w.Write (String.Format("{0, -7:0.00} ", ((PZPoint) _cpArray[i]).x));
				w.Write (String.Format("{0, -7:0.00} ", ((PZPoint) _cpArray[i]).y));
				w.Write (String.Format("{0, -7:0.00} ", ((PZDirection) _vdArray[i]).x));
				w.Write (String.Format("{0, -7:0.00} ", ((PZDirection) _vdArray[i]).y));
				w.Write (String.Format("{0, -7:0.00} ", ((PZPoint) _ep1Array[i]).x));
				w.Write (String.Format("{0, -7:0.00} ", ((PZPoint) _ep1Array[i]).y));
				w.Write (String.Format("{0, -7:0.00} ", ((PZPoint) _ep2Array[i]).x));
				w.Write (String.Format("{0, -7:0.00} ", ((PZPoint) _ep2Array[i]).y));
				w.Write (String.Format("{0, -7:0.000} ", (double) _rArray[i]));
				w.Write (String.Format("{0, -7:0.000} ", (double) _muArray[i]));
				w.WriteLine (String.Format("{0, -7:0.00}", (double) _BArray[i]));
			}
			w.Close();
			fs.Close();
		} // Write()

		/// <summary>
		/// read from file
		/// </summary>
		/// <param name="filename"></param>
		public void Read (string filename)
		{
            int j;
            // clear ArrayList;
            _rootIndexList.Clear();
			_cpArray.Clear();
			_ep1Array.Clear();
			_ep2Array.Clear();
			_rArray.Clear();
			_muArray.Clear();
			_BArray.Clear();
            _vdArray.Clear();

            // read in a delimited file
            string[] fields;
            string[] firstLineDelimiter = new string[] { " ", ",", "(", ")" };
            string[] delimiter = new string[] { " " };
            string[] trackResult = new string[12];
            bool secondLine = true;
            using (Microsoft.VisualBasic.FileIO.TextFieldParser parser = new Microsoft.VisualBasic.FileIO.TextFieldParser(filename))
            {
                // first line
                _layer = Convert.ToInt16(parser.ReadLine());
                // second line
                _index = Convert.ToInt16(parser.ReadLine());
                // third line
                parser.Delimiters = new string[] { " " };
                fields = parser.ReadFields();
                for (int i = 0; i < fields.Length; i++)
                {
                    if (fields[i] == "")
                        continue;
                    else
                    {
                        _rootIndexList.Add(Convert.ToInt16(fields[i]));
                    }
                }                

                //parser.Delimiters = delimiter;
                while (!parser.EndOfData)
                {                   
                    if (secondLine)
                    // second line
                    {
                        parser.Delimiters = firstLineDelimiter;
                        fields = parser.ReadFields();
                        j = 0;
                        for (int i = 0; i < fields.Length; i++)
                        {
                            if (fields[i] == "")
                                continue;
                            else
                            {
                                trackResult[j] = fields[i];
                                j++;
                            }
                        }
                        double cpx = Convert.ToDouble(trackResult[0]);
                        double cpy = Convert.ToDouble(trackResult[1]);
                        double vdx = Convert.ToDouble(trackResult[2]);
                        double vdy = Convert.ToDouble(trackResult[3]);
                        _startCentrePoint = new PZPoint(cpx, cpy);
                        _startVesselDirection = new PZDirection(vdx, vdy);
                        secondLine = false;
                    }
                    else
                        // rest of lines
                    {
                        parser.Delimiters = delimiter;
                        //Read in the fields for the current line
                        fields = parser.ReadFields();
                        j = 0;
                        for (int i = 0; i < fields.Length; i++)
                        {
                            if (fields[i] == "")
                                continue;
                            else
                            {
                                trackResult[j] = fields[i];
                                j++;
                            }
                        }
                        // read to arrays
                        double cpx = Convert.ToDouble(trackResult[1]);
                        double cpy = Convert.ToDouble(trackResult[2]);
                        double vdx = Convert.ToDouble(trackResult[3]);
                        double vdy = Convert.ToDouble(trackResult[4]);
                        double ep1x = Convert.ToDouble(trackResult[5]);
                        double ep1y = Convert.ToDouble(trackResult[6]);
                        double ep2x = Convert.ToDouble(trackResult[7]);
                        double ep2y = Convert.ToDouble(trackResult[8]);
                        double inputr = Convert.ToDouble(trackResult[9]);
                        double inputmu = Convert.ToDouble(trackResult[10]);
                        double inputB = Convert.ToDouble(trackResult[11]);
                        PZPoint cp = new PZPoint(cpx, cpy);
                        PZPoint ep1 = new PZPoint(ep1x, ep1y);
                        PZPoint ep2 = new PZPoint(ep2x, ep2y);
                        PZDirection vd = new PZDirection(vdx, vdy);
                        _cpArray.Add(cp);
                        _ep1Array.Add(ep1);
                        _ep2Array.Add(ep2);
                        _vdArray.Add(vd);
                        _rArray.Add(inputr);
                        _muArray.Add(inputmu);
                        _BArray.Add(inputB);
                    }
                }
            }
            _count = _cpArray.Count;
		} // Read()

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(String.Format("{0, 5}", _index));
            sb.Append("{");
            foreach (int rootIndex in _rootIndexList)
                sb.Append(String.Format("{0, 5}", rootIndex));
            //sb.Append(String.Format("{0, 5}", _rootIndex));
            //sb.Append(String.Format("{0, 5}", _layer));
            sb.Append("}");
            sb.Append(String.Format("{0, 8:F2}", MeanR()));
            sb.Append(String.Format("{0, 8:F2}", _cpArray.Count));
            sb.Append(String.Format("{0, 8:F2}", _flow));
            sb.Append(String.Format("{0, 8:F2}", _hf));
            return sb.ToString();
        } // ToString()

		public bool Exit()
		{
			bool r_exit = false;
			bool mu_exit = false;
			// check r and mu
			_mean_r = 0;
			_mean_mu = 0;
			_mean_B = 0;
			for (int i = 0; i < _rArray.Count - 1; i ++)
			{
				_mean_r += (double) _rArray[i];
				_mean_mu += (double) _muArray[i];
				_mean_B += (double) _BArray[i];
			}
			_mean_r /= (double) (_rArray.Count - 1);
			_mean_mu /= (double) (_muArray.Count - 1);
			_mean_B /= (double) (_BArray.Count - 1);
			
			if ((double) _rArray[_rArray.Count - 1] > _mean_r + 1.5) 
			{
				r_exit = true;
				_exitMsg = "r > mean + 1.5";
			}
			/*
			if ((double) muArray[muArray.Count - 1] > mean_mu * 1.5)
			{
				mu_exit = true;
				exit_msg = "mu > 1.5 mean";
			}
			*/
			if	((double) _muArray[_muArray.Count - 1] < _mean_mu / 10.0)
			{
				mu_exit = true;
				_exitMsg = "mu < mean / 10.0";
			}
			if (r_exit || mu_exit)
				return true;
			else
				return false;
		} // Exit()

        #region stat method
        public double MeanB()
        {
            double meanB = 0.0;
            for (int i = 0; i < _count; i++)
                meanB += _BArray[i];
            meanB /= (double)_count;
            _mean_B = meanB;
            return meanB;
        }

        public double MeanR()
        {
            double meanR = 0.0;
            int count = _rArray.Count;
            for (int i = 0; i < count; i++)
                meanR += _rArray[i];
            meanR /= (double)count;
            _mean_r = meanR;
            return meanR;
        }
        #endregion

    }
}
